﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
//using Common.Logging;
using NPMonitor.Helpers;
using NPMonitor.Interfaces.Internal;
using NPMonitor.DataModel.Internal;
using NPMonitor.Implementation.Internal;
using NPMonitor.Interfaces;
using NPMonitor.Implementation;
using NPMonitor.DataModel;

namespace NPMonitor.Launcher.Tests.Service
{
    [TestClass]
    public class ExecutionQueueTest
    {
        public event EventHandler<NotificationEventArgs> Notify;

        IJobManager jobManager;
        Timer pollTimer;
        ServiceStatus serviceStatus = ServiceStatus.Stopped;
        //ILog logger;
        IExecutionQueue queue = null;

        [TestInitialize]
        public void Setup()
        {
            pollTimer = new Timer();
            jobManager = new JobManager();
            //logger = jobManager.Logger;
            queue = new SequencedExecutionQueue();
            pollTimer.Elapsed += new ElapsedEventHandler(pollTimer_Elapsed);
        }

        [TestMethod]
        public void SequencedExecutionQueueTest()
        {
            IExecutionQueue sequQueue = new SequencedExecutionQueue();
            IJobManager jobManager = new JobManager();

            JobData jobData = StoreManager.Current.JobStore.GetScheduleReadyJobs("Test-001", DateTime.Now).FirstOrDefault();

            while (jobData != null)
            {
                sequQueue.Enqueue(new JobContext(jobManager, jobData, null));
                System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1));  // Sleep for 10 sec
                jobData = StoreManager.Current.JobStore.GetScheduleReadyJobs("Test-001", DateTime.Now).FirstOrDefault();
            }

            Assert.IsNull(jobData);
        }

        #region Private Members

        void pollTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            pollTimer.Stop();

            try
            {
                DateTime now = Helpers.Utils.CurrentServerUtcTime;
                var jobsScheduledToExecute = jobManager.JobStore.GetScheduleReadyJobs("Ray-PC", now);

                var jobIdsToExecute = new List<long>();
                if (jobsScheduledToExecute != null && jobsScheduledToExecute.Count > 0)
                {
                    foreach (var job in jobsScheduledToExecute)
                    {
                        if (job.Schedule != null && job.JobSchedule.CanOccurAt(now))
                        {
                            jobIdsToExecute.Add(job.Id);
                        }
                    }

                    jobManager.JobStore.SetJobStatuses(jobIdsToExecute.ToArray(), JobStatus.Scheduled, JobStatus.Ready, null, null);
                }
            }
            catch (Exception ex)
            {
                //logger.Info("PollTimer Elapsed threw an exception.", ex);
            }
            finally
            {
                if (serviceStatus == ServiceStatus.Running)
                {
                    CheckForJobEnqueue();

                    pollTimer.Start();
                }
            }
        }

        /// <summary>
        /// This method runs through all the queues and checks for open space.  If there is open space and there are ready jobs for that queue, it gets enqueued.
        /// </summary>
        private void CheckForJobEnqueue()
        {
            if (serviceStatus == ServiceStatus.Running)
            {
                if (queue.ActiveThreads < queue.ThreadCount)
                {
                    var readyJobs = jobManager.JobStore.QueueReadyJobs(queue.Id, queue.ThreadCount - queue.ActiveThreads, "Ray-PC");

                    foreach (var readyJob in readyJobs)
                    {
                        if (EnqueueJobForExecution(readyJob))
                        {
                            TriggerNotify(string.Format("Job {0} of name '{1}' has been queued for execution.", readyJob.Id, readyJob.Name));
                        }
                        else
                        {
                            //Queue Full
                            jobManager.JobStore.DequeueJob(readyJob);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Creates an instance of the IJob and queues it on it's specified queue for execution.
        /// </summary>
        /// <param name="jobData">The job data.</param>
        /// <returns></returns>
        private bool EnqueueJobForExecution(JobData jobData)
        {
            if (serviceStatus == ServiceStatus.Running)
            {
                lock (jobData)
                {
                    if (jobData.Status == JobStatus.Ready || jobData.Status == JobStatus.Queuing || jobData.Status == JobStatus.Queued)
                    {
                        return queue.Enqueue(new JobContext(jobManager, jobData, null));
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Triggers the notifification event to notify listeners about interesting events.
        /// </summary>
        /// <param name="message">The notification message.</param>
        private void TriggerNotify(string message)
        {
            if (Notify != null)
            {
                Notify(this, new NotificationEventArgs(message));
            }
        }

        #endregion
    }
}
